/**
 * 
 */
package edu.fiu.cis.selfhealingdisk;


/**
 * @author vanvorst
 * 
 */
public class IORequest implements Comparable<IORequest>{
	
	
	
	public enum IOEnum {
		READ("R", 1), WRITE("W", 0);
		public static IOEnum parse(final String t) {
			return "R".equalsIgnoreCase(t) ? READ : WRITE;
		}
		private final String s;
		private final int i;

		private IOEnum(final String s, final int i) {
			this.s = s;
			this.i = i;
		}

		public int asInt() {
			return this.i;
		}

		@Override
		public String toString() {
			return this.s;
		}
	}

	private static int totalQRequests = 0;;

	/**
	 * Parse IO request from a string in the form : [timestamp] [pid]
	 * [executable] [lba] [size] [R/W] [major-device] [minor-device]
	 * [content-hash]
	 * 
	 * @param str
	 * @return
	 */
	public static IORequest parseIORequest(final String str) {
		final String[] toks = str.trim().split("\\s");
		if (toks.length != 9) {
			throw new RuntimeException("Invalid IORequest:\n" + str);
		}
		return new IORequest(Long.parseLong(toks[0]),
				Integer.parseInt(toks[1]), toks[2], Integer.parseInt(toks[3]),
				Integer.parseInt(toks[4]), IOEnum.parse(toks[5]), Integer
						.parseInt(toks[6]), Integer.parseInt(toks[7]), toks[8], false);
	}

	private long requestTime;
	private final int pid;
	private final String executable;
	private final long lba;
	private final int size;
	private final IOEnum type;
	private final int majorDevice;
	private final int minorDevice;
	private final String contentHash;
	private final long requestId;
	
	private final boolean isParity;

	private long serviceTime = -1;

	/**
	 * @param requestTime
	 * @param pid
	 * @param executable
	 * @param lba
	 * @param size
	 * @param type
	 * @param majorDevice
	 * @param minorDevice
	 * @param contentHash
	 */
	public IORequest(final long requestTime, final int pid,
			final String executable, final long lba, final int size,
			final IOEnum type, final int majorDevice, final int minorDevice,
			final String contentHash,boolean isParity) {
		this.requestTime = requestTime;
		this.pid = pid;
		this.executable = executable;
		this.lba = lba;
		this.size = size;
		this.type = type;
		this.majorDevice = majorDevice;
		this.minorDevice = minorDevice;
		this.contentHash = contentHash;
		this.isParity = isParity; 
		this.requestId = ++IORequest.totalQRequests;
	}
	
	
	/**
	 * @param requestTime
	 * @param pid
	 * @param executable
	 * @param lba
	 * @param size
	 * @param type
	 * @param majorDevice
	 * @param minorDevice
	 * @param contentHash
	 */
	public IORequest(final long requestTime,final long lba, final int size,final IOEnum type,boolean isParity) {
		this.requestTime = requestTime;
		this.pid = 0;
		this.executable = null;
		this.lba = lba;
		this.size = size;
		this.type = type;
		this.majorDevice = 0;
		this.minorDevice = 0;
		this.contentHash = null;
		this.isParity = isParity;
		this.requestId = ++IORequest.totalQRequests;
	}
	
	
	

	/**
	 * Returns the request formated for a BlkParse IO Trace: [timestamp] [pid]
	 * [executable] [lba] [size] [R/W] [major-device] [minor-device]
	 * [content-hash]
	 * 
	 * @return
	 */
	public String asBlkParseEntry() {
		return this.asBlkParseEntry(false);
	}
	
	/**
	 * Returns the request formated for a BlkParse IO Trace: 
	 * [request timestamp] [service timestamp] [pid] [executable] [lba] [size] [R/W] [major-device] [minor-device] [content-hash]
	 * 
	 * @param includeServiceTime
	 * @return
	 */
	public String asBlkParseEntry(boolean includeServiceTime) {
		return Long.toString(this.requestTime) + " "
				+ (includeServiceTime?Long.toString(this.serviceTime)+" ":"")
				+ Integer.toString(this.pid) + " " + this.executable + " "
				+ Long.toString(this.lba) + " "
				+ Integer.toString(this.size) + " " + this.type + " "
				+ Integer.toString(this.majorDevice) + " "
				+ Integer.toString(this.minorDevice) + " " + this.contentHash;
	}

	/**
	 * Returns the request formated for a DiskSIM IO Trace: 
	 * 
	 * [arrival time] [device #] [block #] [size] [flags] 
	 * 
	 * arrival time: Float[nonnegative milliseconds] 
	 * specifying the time the request “arrives�?
	 * relative to the start of the simulation (at time 0.0). Requests must
	 * appear in the input stream in ascending time order.
	 * 
	 * device #: Integer specifying the device number (i.e., the storage
	 * component that the request accesses). The device mappings (see Section
	 * 3), if any, are applied to this value.
	 * 
	 * block #: Integer [nonnegative] specifying the first device address of the
	 * request. The value is specified in the appropriate access unit of the
	 * logical device in question, which may be modified by the device mappings
	 * (see Section 3).
	 * 
	 * size: Integer [positive] specifying the size of the request in device
	 * blocks (i.e., the access unit of the logical device in question).
	 * 
	 * flags: Hex Integer comprising a Boolean Bitfield specifying additional
	 * information about the request. For example, bit 0 indicates whether the
	 * request is a read (1) or a write (0). Other bits specify information that
	 * is most appropriate to a full system simulation environment (e.g.,
	 * request priority). Valid bitfield values are listed
	 * 
	 * 
	 * @param startTime
	 *            The time (in milliseconds) of the first IO request.
	 * @return
	 */
	public String asDiskSimIOEntry(final long startTime) {
		return this.asDiskSimIOEntry(startTime, true);
	}

	/**
 	 * [arrival time] [service time] [device #] [block #] [size] [flags] 
	 * @param startTime
	 * @param includeServiceTime
	 * @return
	 */
	public String asDiskSimIOEntry(final long startTime, boolean includeServiceTime) {
		return Long.toString(this.requestTime) + " " 
				+ Long.toString(this.serviceTime)+" " 
				+ Long.toString(this.lba) + " "
				+ Integer.toString(this.size) + " "
				+ Integer.toString(this.type.asInt()) + " "
				+ Integer.toString(((this.isParity)?1:0));
	}
	
	/**
	 * @return the contentHash
	 */
	public final String getContentHash() {
		return this.contentHash;
	}

	/**
	 * @return the executable
	 */
	public final String getExecutable() {
		return this.executable;
	}

	/**
	 * @return the lba
	 */
	public final long getLba() {
		return this.lba;
	}

	/**
	 * @return the majorDevice
	 */
	public final int getMajorDevice() {
		return this.majorDevice;
	}

	/**
	 * @return the minorDevice
	 */
	public final int getMinorDevice() {
		return this.minorDevice;
	}

	/**
	 * @return the pid
	 */
	public final int getPid() {
		return this.pid;
	}

	/**
	 * @return the requestId
	 */
	public final long getRequestId() {
		return this.requestId;
	}

	/**
	 * @return the requestTime
	 */
	public final long getRequestTime() {
		return this.requestTime;
	}

	/**
	 * @return the serviceTime
	 */
	public final long getServiceTime() {
		return this.serviceTime;
	}

	/**
	 * @return the size
	 */
	public final int getSize() {
		return this.size;
	}

	/**
	 * @return the type
	 */
	public final IOEnum getType() {
		return this.type;
	}

	/**
	 * @param requestTime
	 *            the requestTime to set
	 */
	public final void setRequestTime(final long requestTime) {
		this.requestTime = requestTime;
	}

	/**
	 * @param serviceTime
	 *            the serviceTime to set
	 */
	public final void setServiceTime(final long serviceTime) {
		this.serviceTime = serviceTime;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return this.asBlkParseEntry(
	);
	}
	
	public int compareTo(IORequest o) {
		if (this.requestTime < o.requestTime) return -1;
		if (this.requestTime > o.requestTime) return 1;
		return 0;
	}


	public boolean isParity() {
		return isParity;
	}
	
}
